#pragma once

#include <ddblk/locks/nlock_int.h>

typedef struct _mcsnode
{
	struct _mcsnode *next;
	lkword_t enabled;
} _mcsnode_t;

typedef struct _mcslock
{
	_mcsnode_t *tail;
} _mcslock_t;


DECL_NLOCK(mcs)

/* 解除节点分配器耦合 */
SAI _mcslock_t *__mcs_init(_mcslock_t *s)
{
	s->tail = NULL;
	return s;
}
SAI void *__mcs_destruct(_mcslock_t *s)
{
	ASSERT(s->tail == NULL);	/* 肯定的 */
	return (void *)s;
}
SAI void __mcs_lock(_mcslock_t *s, 
	INFUN(nlock_wait, waitpolicy), _mcsnode_t *node, const acq_ord_t order){
	waitcnt_t cnt = -1;
	/* 断言 */
	ASSERT(node->next == (_mcsnode_t *)NULL);
	ASSERT(node->enabled == FALSE);
	lkword_t node_disabled = 0;
	_mcsnode_t *pred = swp_rel(&s->tail, node);

	if (pred != NULL)
	{
		atm_st(&pred->next, node);
		while (!atm_ld(&node->enabled))
		{
			cnt = waitpolicy->waitf(&node->enabled, &node_disabled, cnt);
			/* 不能返回FALSE, 因为有可能会死锁? */
			// if (! cnt) return FALSE;
		}
	}
	if (order == LACQ) mp_rmb();
	
}
SAI void __mcs_unlock(_mcslock_t *s,
	INFUN(nlock_wait, waitpolicy), _mcsnode_t *node, const rel_ord_t order)
{
	_mcsnode_t *succ;
	_mcsnode_t *old_node;
	lkword_t node_true = 1;
	succ = node->next;

	if (succ == NULL)
	{
		old_node = node;
		word_t ret;

		if (order == RREL)
			ret = cas_rel(&s->tail, &old_node, NULL);
		else
			ret = cas(&s->tail, &old_node, NULL);

		if (ret)
		{
			/* 不需要通知, 直接返回 */
			return ;
		}
	}

	while (succ == NULL)
	{
		succ = atm_ld(&node->next);
		if (!succ){
			CPU_RELAX();
		}
	}

	if (order == RREL)
		atm_strel(&succ->enabled, TRUE);
	else
		atm_st(&succ->enabled, TRUE);
	
	/* 通知等待线程 */
	waitpolicy->wakef(&succ->enabled, &node_true);
}


PARAM_NONULL(1) SAI _mcslock_t *_mcs_init(void *ptr, size_t size, INFUN(nlock_lknode, lknode))
{	
	PARAM_USED(lknode);
	if (size < sizeof(_mcslock_t)) return NULL;
	_mcslock_t *s = (_mcslock_t *)ptr;
	__mcs_init(s);
	return s;
}
PARAM_NONULL(1) SAI void *_mcs_destruct(_mcslock_t *s, INFUN(nlock_lknode, lknode))
{
	PARAM_USED(s, lknode);
	return __mcs_destruct(s);
}

PARAM_NONULL(1) SAI bool_t _mcs_lock(_mcslock_t *s, locktoken_t *ptoken, waitcnt_t cnt, 
	INFUN(nlock_wait, waitpolicy), INFUN(nlock_lknode, lknode), const acq_ord_t order)
{
	PARAM_USED(cnt);
	_mcsnode_t *node = lknode->get_lknode(sizeof(_mcsnode_t));
	atm_st(&node->next, (_mcsnode_t *)NULL);
	atm_st(&node->enabled, FALSE);
	*ptoken = node;
	
	__mcs_lock(s, waitpolicy, node, order);
	return TRUE;
}

PARAM_NONULL(1, 2) SAI bool_t _mcs_unlock(_mcslock_t *s, locktoken_t token, waitcnt_t cnt, 
	INFUN(nlock_wait, waitpolicy),  INFUN(nlock_lknode, lknode), const rel_ord_t order)
{
	PARAM_USED(cnt);
	
	_mcsnode_t *node = (_mcsnode_t *)token;

	__mcs_unlock(s, waitpolicy, node, order);
	/* 归还节点 */
	lknode->ret_lknode(node, sizeof(_mcsnode_t));

	return TRUE;
}

